From e7002b8a540f438f73f64b4a2301999054bef394 Mon Sep 17 00:00:00 2001 From: tsteven4 <13596209+tsteven4@users.noreply.github.com> Date: Thu, 30 Jan 2020 11:06:25 -0700 Subject: [PATCH] convert xcsv format to sub-class of Format. (#481) * convert xcsv format to sub-class of Format. * move XcsvStyle functions inside class. * Unnest XcsvStyle. * remove redundant qualifiers. --- vecs.cc | 10 +- vecs.h | 6 +- xcsv.cc | 277 +++++++++++++++++----------------------------------- xcsv.h | 295 ++++++++++++++++++++++++++++++++++++++++++++++---------- 4 files changed, 341 insertions(+), 247 deletions(-) diff --git a/vecs.cc b/vecs.cc index 500ebb80d..9878279f2 100644 --- a/vecs.cc +++ b/vecs.cc @@ -269,7 +269,7 @@ Format* Vecs::find_vec(const QString& vecname) * format that utilized an internal style file, then we need to let * xcsv know the internal style file is no longer in play. */ - xcsv_setup_internal_style(nullptr); + xcsv_fmt.xcsv_setup_internal_style(nullptr); #endif // CSVFMTS_ENABLED vec.vec->set_name(vec.name); /* needed for session information */ return vec.vec; @@ -315,7 +315,7 @@ Format* Vecs::find_vec(const QString& vecname) disp_vec_options(svec.name, xcsv_args); } #if CSVFMTS_ENABLED - xcsv_setup_internal_style(svec.style_buf); + xcsv_fmt.xcsv_setup_internal_style(svec.style_buf); #endif // CSVFMTS_ENABLED vec_list[0].vec->set_name(svec.name); /* needed for session information */ @@ -404,7 +404,7 @@ QVector Vecs::sort_and_unify_vecs() const /* Gather the relevant info for the style based formats. */ for (const auto& svec : style_list) { - XcsvStyle style = xcsv_read_internal_style(svec.style_buf); + XcsvStyle style = XcsvStyle::xcsv_read_internal_style(svec.style_buf); vecinfo_t info; info.name = svec.name; info.desc = style.description; @@ -431,9 +431,9 @@ QVector Vecs::sort_and_unify_vecs() const * 'Full path to XCSV style file' argument to any * GUIs for an internal format. */ - bool first = true; const QVector* args = vec_list.at(0).vec->get_args(); if (args != nullptr) { + bool first = true; for (const auto& arg : *args) { if (!first) { info.arginfo.append(arginfo_t(arg)); @@ -673,7 +673,7 @@ bool Vecs::validate_args(const QString& name, const QVector* args) return ok; } -bool Vecs::validate_vec(const Vecs::vecs_t& vec) +bool Vecs::validate_vec(const vecs_t& vec) { bool ok = validate_args(vec.name, vec.vec->get_args()); diff --git a/vecs.h b/vecs.h index 2d2870a6a..e97a2f593 100644 --- a/vecs.h +++ b/vecs.h @@ -33,11 +33,9 @@ #include "gpx.h" #include "legacyformat.h" #include "mynav.h" +#include "xcsv.h" -#if CSVFMTS_ENABLED -extern ff_vecs_t xcsv_vecs; -#endif // CSVFMTS_ENABLED extern ff_vecs_t geo_vecs; extern ff_vecs_t mag_svecs; extern ff_vecs_t mag_fvecs; @@ -268,7 +266,7 @@ private: * of this class is constructed. */ #if CSVFMTS_ENABLED - LegacyFormat xcsv_fmt {xcsv_vecs}; + XcsvFormat xcsv_fmt; #endif // CSVFMTS_ENABLED LegacyFormat geo_fmt {geo_vecs}; GpxFormat gpx_fmt; diff --git a/xcsv.cc b/xcsv.cc index f2d87c3a5..d61d9f73a 100644 --- a/xcsv.cc +++ b/xcsv.cc @@ -44,7 +44,6 @@ #include // for QStringList #include // for QTextStream #include // for QTime -#include // for QVector #include // for qAsConst, QAddConst<>::Type, qPrintable #include "defs.h" @@ -62,17 +61,10 @@ #include "strptime.h" // for strptime #include "xcsv.h" -#define MYNAME "XCSV" - -/* macros */ -constexpr char lat_dir(double a) {return a < 0.0 ? 'S' : 'N';} -constexpr char lon_dir(double a) {return a < 0.0 ? 'W' : 'E';} -/* convert excel time (days since 1900) to time_t and back again */ -constexpr double excel_to_timet(double a) {return (a - 25569.0) * 86400.0;} -constexpr double timet_to_excel(double a) {return (a / 86400.0) + 25569.0;} +#if CSVFMTS_ENABLED -constexpr int gps_datum_wgs84 = 118; // GPS_Lookup_Datum_Index("WGS 84") +#define MYNAME "XCSV" /* * Internal numeric value to associate with each keyword in a style file. @@ -173,91 +165,8 @@ enum xcsv_token { #include "xcsv_tokens.gperf" // for Perfect_Hash, xt_mapping -#if CSVFMTS_ENABLED -/****************************************************************************/ -/* obligatory global struct */ -/****************************************************************************/ - -static XcsvFile* xcsv_file; -static const XcsvStyle* xcsv_style; -static double pathdist = 0; -static double oldlon = 999; -static double oldlat = 999; - -static int waypt_out_count = 0; -static const route_head* csv_track = nullptr; -static const route_head* csv_route = nullptr; - -struct xcsv_parse_data { - QString rte_name; - QString trk_name; - bool new_track{false}; - double utm_northing{0}; - double utm_easting{0}; - double utm_zone{0}; - char utm_zonec{'N'}; - UrlLink* link_{nullptr}; - gpsbabel_optional::optional lat_dir_positive; - gpsbabel_optional::optional lon_dir_positive; -}; - -static char* styleopt = nullptr; -static char* snlenopt = nullptr; -static char* snwhiteopt = nullptr; -static char* snupperopt = nullptr; -static char* snuniqueopt = nullptr; -static char* prefer_shortnames = nullptr; -static char* xcsv_urlbase = nullptr; -static char* opt_datum = nullptr;; - -static const char* intstylebuf = nullptr; - -static -QVector xcsv_args = { - { - "style", &styleopt, "Full path to XCSV style file", nullptr, - ARGTYPE_FILE | ARGTYPE_REQUIRED, ARG_NOMINMAX, nullptr - }, - { - "snlen", &snlenopt, "Max synthesized shortname length", nullptr, - ARGTYPE_INT, "1", nullptr, nullptr - }, - { - "snwhite", &snwhiteopt, "Allow whitespace synth. shortnames", - nullptr, ARGTYPE_BOOL, ARG_NOMINMAX, nullptr - }, - { - "snupper", &snupperopt, "UPPERCASE synth. shortnames", - nullptr, ARGTYPE_BOOL, ARG_NOMINMAX, nullptr - }, - { - "snunique", &snuniqueopt, "Make synth. shortnames unique", - nullptr, ARGTYPE_BOOL, ARG_NOMINMAX, nullptr - }, - { - "urlbase", &xcsv_urlbase, "Basename prepended to URL on output", - nullptr, ARGTYPE_STRING, ARG_NOMINMAX, nullptr - }, - { - "prefer_shortnames", &prefer_shortnames, - "Use shortname instead of description", - nullptr, ARGTYPE_BOOL, ARG_NOMINMAX, nullptr - }, - { - "datum", &opt_datum, "GPS datum (def. WGS 84)", - nullptr, ARGTYPE_STRING, ARG_NOMINMAX, nullptr - }, -}; - -/* something to map config file constants to chars */ -struct char_map_t { - const QString key; - const QString chars; -}; - /* a table of config file constants mapped to chars */ -static -char_map_t xcsv_char_table[] = { +const XcsvStyle::char_map_t XcsvStyle::xcsv_char_table[] = { { "COMMA", "," }, { "COMMASPACE", ", " }, { "SINGLEQUOTE", "'" }, @@ -276,12 +185,12 @@ char_map_t xcsv_char_table[] = { }; // Given a keyword of "COMMASPACE", return ", ". -static QString -xcsv_get_char_from_constant_table(const QString& key) +QString +XcsvStyle::xcsv_get_char_from_constant_table(const QString& key) { static QHash substitutions; if (substitutions.empty()) { - for (char_map_t* cm = xcsv_char_table; !cm->key.isNull(); cm++) { + for (const char_map_t* cm = xcsv_char_table; !cm->key.isNull(); cm++) { substitutions.insert(cm->key, cm->chars); } } @@ -294,14 +203,14 @@ xcsv_get_char_from_constant_table(const QString& key) // Remove outer quotes. // Should probably be in csv_util. -static QString dequote(const QString& in) { +QString XcsvStyle::dequote(const QString& in) { QString r = in.simplified(); if (r.startsWith("\"")) r = r.mid(1); if (r.endsWith("\"")) r.chop(1); return r; } -static void validate_fieldmap(const field_map& fmp, bool is_output) { +void XcsvStyle::validate_fieldmap(const field_map& fmp, bool is_output) { if (fmp.key.isEmpty()) { Fatal() << MYNAME << ": xcsv style is missing" << (is_output ? "output" : "input") << "field type."; @@ -318,8 +227,8 @@ static void validate_fieldmap(const field_map& fmp, bool is_output) { /* xcsv_ifield_add() - add input field to ifield queue. */ /* usage: xcsv_ifield_add("DESCRIPTION", "", "%s") */ /*****************************************************************************/ -static void -xcsv_ifield_add(XcsvStyle* style, const QString& qkey, const QString& qval, const QString& qpfc) +void +XcsvStyle::xcsv_ifield_add(XcsvStyle* style, const QString& qkey, const QString& qval, const QString& qpfc) { QByteArray key = qkey.toUtf8(); QByteArray val = qval.toUtf8(); @@ -337,8 +246,8 @@ xcsv_ifield_add(XcsvStyle* style, const QString& qkey, const QString& qval, cons /* xcsv_ofield_add() - add output field to ofield queue. */ /* usage: xcsv_ofield_add("LAT_DECIMAL", "", "%08.5lf") */ /*****************************************************************************/ -static void -xcsv_ofield_add(XcsvStyle* style, const QString& qkey, const QString& qval, const QString& qpfc, unsigned options) +void +XcsvStyle::xcsv_ofield_add(XcsvStyle* style, const QString& qkey, const QString& qval, const QString& qpfc, unsigned options) { QByteArray key = qkey.toUtf8(); QByteArray val = qval.toUtf8(); @@ -352,8 +261,8 @@ xcsv_ofield_add(XcsvStyle* style, const QString& qkey, const QString& qval, cons style->ofields.append(fmp); } -static QDateTime -yyyymmdd_to_time(const char* s) +QDateTime +XcsvFormat::yyyymmdd_to_time(const char* s) { QDate d = QDate::fromString(s, "yyyyMMdd"); return QDateTime(d); @@ -363,8 +272,8 @@ yyyymmdd_to_time(const char* s) /* * sscanftime - Parse a date buffer using strftime format */ -static time_t -sscanftime(const char* s, const char* format, const int gmt) +time_t +XcsvFormat::sscanftime(const char* s, const char* format, const int gmt) { struct tm stm; memset(&stm, 0, sizeof(stm)); @@ -390,8 +299,8 @@ sscanftime(const char* s, const char* format, const int gmt) return 0; } -static time_t -addhms(const char* s, const char* format) +time_t +XcsvFormat::addhms(const char* s, const char* format) { time_t tt = 0; int hour = 0; @@ -412,8 +321,8 @@ addhms(const char* s, const char* format) return tt; } -static QString -writetime(const char* format, time_t t, bool gmt) +QString +XcsvFormat::writetime(const char* format, time_t t, bool gmt) { static struct tm* stmp; @@ -432,14 +341,14 @@ writetime(const char* format, time_t t, bool gmt) return QString(tbuff); } -static QString -writetime(const char* format, const gpsbabel::DateTime& t, bool gmt) +QString +XcsvFormat::writetime(const char* format, const gpsbabel::DateTime& t, bool gmt) { return writetime(format, t.toTime_t(), gmt); } -static QString -writehms(const char* format, time_t t, int gmt) +QString +XcsvFormat::writehms(const char* format, time_t t, int gmt) { static struct tm no_time = tm(); static struct tm* stmp = &no_time; @@ -459,21 +368,21 @@ writehms(const char* format, time_t t, int gmt) (stmp->tm_hour >= 12 ? "PM" : "AM")); } -static QString -writehms(const char* format, const gpsbabel::DateTime& t, int gmt) +QString +XcsvFormat::writehms(const char* format, const gpsbabel::DateTime& t, int gmt) { return writehms(format, t.toTime_t(), gmt); } -static long -time_to_yyyymmdd(const QDateTime& t) +long +XcsvFormat::time_to_yyyymmdd(const QDateTime& t) { QDate d = t.date(); return d.year() * 10000 + d.month() * 100 + d.day(); } -static garmin_fs_t* -gmsd_init(Waypoint* wpt) +garmin_fs_t* +XcsvFormat::gmsd_init(Waypoint* wpt) { garmin_fs_t* gmsd = garmin_fs_t::find(wpt); if (gmsd == nullptr) { @@ -487,8 +396,8 @@ gmsd_init(Waypoint* wpt) /* xcsv_parse_val() - parse incoming data into the waypt structure. */ /* usage: xcsv_parse_val("-123.34", *waypt, *field_map) */ /*****************************************************************************/ -static void -xcsv_parse_val(const QString& value, Waypoint* wpt, const field_map& fmp, +void +XcsvFormat::xcsv_parse_val(const QString& value, Waypoint* wpt, const XcsvStyle::field_map& fmp, xcsv_parse_data* parse_data, const int line_no) { const char* enclosure = ""; @@ -913,11 +822,11 @@ xcsv_parse_val(const QString& value, Waypoint* wpt, const field_map& fmp, } /*****************************************************************************/ -/* xcsv_data_read() - read input file, parsing lines, fields and handling */ +/* read() - read input file, parsing lines, fields and handling */ /* any data conversion (the input meat) */ /*****************************************************************************/ -static void -xcsv_data_read() +void +XcsvFormat::read() { int linecount = 0; route_head* rte = nullptr; @@ -967,7 +876,7 @@ xcsv_data_read() /* now rip the line apart */ for (const auto& value : values) { - const field_map& fmp = xcsv_style->ifields.at(ifield_idx++); + const XcsvStyle::field_map& fmp = xcsv_style->ifields.at(ifield_idx++); xcsv_parse_val(value, wpt_tmp, fmp, &parse_data, linecount); if (ifield_idx >= xcsv_style->ifields.size()) { @@ -1037,8 +946,8 @@ xcsv_data_read() } } -static void -xcsv_resetpathlen(const route_head* head) +void +XcsvFormat::xcsv_resetpathlen(const route_head* head) { pathdist = 0; oldlat = 999; @@ -1060,8 +969,8 @@ xcsv_resetpathlen(const route_head* head) /* xcsv_waypt_pr() - write output file, handling output conversions */ /* (the output meat) */ /*****************************************************************************/ -static void -xcsv_waypt_pr(const Waypoint* wpt) +void +XcsvFormat::xcsv_waypt_pr(const Waypoint* wpt) { QString buff; double latitude, longitude; @@ -1132,11 +1041,11 @@ xcsv_waypt_pr(const Waypoint* wpt) */ int field_is_unknown = 0; - if ((i != 0) && !(fmp.options & options_nodelim)) { + if ((i != 0) && !(fmp.options & XcsvStyle::options_nodelim)) { xcsv_file->stream << write_delimiter; } - if (fmp.options & options_absolute) { + if (fmp.options & XcsvStyle::options_absolute) { lat = fabs(lat); lon = fabs(lon); } @@ -1152,7 +1061,7 @@ xcsv_waypt_pr(const Waypoint* wpt) buff = QString::asprintf(fmp.printfc.constData(), waypt_out_count + atoi(fmp.val.constData())); break; case XT_CONSTANT: { - auto cp = xcsv_get_char_from_constant_table(fmp.val.constData()); + auto cp = XcsvStyle::xcsv_get_char_from_constant_table(fmp.val.constData()); if (!cp.isEmpty()) { buff = QString::asprintf(fmp.printfc.constData(), CSTR(cp)); } else { @@ -1642,7 +1551,7 @@ xcsv_waypt_pr(const Waypoint* wpt) } QString obuff = csv_stringclean(buff, xcsv_style->badchars); - if (field_is_unknown && fmp.options & options_optional) { + if (field_is_unknown && fmp.options & XcsvStyle::options_optional) { continue; } @@ -1673,8 +1582,8 @@ xcsv_waypt_pr(const Waypoint* wpt) } // return |original| after performing token replacement. -static QString -xcsv_replace_tokens(const QString& original) { +QString +XcsvFormat::xcsv_replace_tokens(const QString& original) const { QString replacement = original; // Don't do potentially expensive replacements if token prefix // isn't present; @@ -1697,11 +1606,11 @@ xcsv_replace_tokens(const QString& original) { } /*****************************************************************************/ -/* xcsv_data_write(void) - write prologues, spawn the output loop, and write */ +/* write(void) - write prologues, spawn the output loop, and write */ /* epilogues. */ /*****************************************************************************/ -static void -xcsv_data_write() +void +XcsvFormat::write() { /* reset the index counter */ waypt_out_count = 0; @@ -1712,14 +1621,21 @@ xcsv_data_write() xcsv_file->stream << line_to_write << xcsv_style->record_delimiter; } + auto xcsv_waypt_pr_lambda = [this](const Waypoint* wpt)->void { + xcsv_waypt_pr(wpt); + }; + auto xcsv_resetpathlen_lambda = [this](const route_head* rte)->void { + xcsv_resetpathlen(rte); + }; + if ((xcsv_style->datatype == 0) || (xcsv_style->datatype == wptdata)) { - waypt_disp_all(xcsv_waypt_pr); + waypt_disp_all(xcsv_waypt_pr_lambda); } if ((xcsv_style->datatype == 0) || (xcsv_style->datatype == rtedata)) { - route_disp_all(xcsv_resetpathlen, nullptr, xcsv_waypt_pr); + route_disp_all(xcsv_resetpathlen_lambda, nullptr, xcsv_waypt_pr_lambda); } if ((xcsv_style->datatype == 0) || (xcsv_style->datatype == trkdata)) { - track_disp_all(xcsv_resetpathlen, nullptr, xcsv_waypt_pr); + track_disp_all(xcsv_resetpathlen_lambda, nullptr, xcsv_waypt_pr_lambda); } /* output epilogue lines, if any. */ @@ -1729,8 +1645,8 @@ xcsv_data_write() } } -static void -xcsv_parse_style_line(XcsvStyle* style, QString line) +void +XcsvStyle::xcsv_parse_style_line(XcsvStyle* style, QString line) { // The lines to be parsed have a leading operation |op| that is // separated by whitespace from the rest. Each op may have zero or @@ -1906,8 +1822,8 @@ xcsv_parse_style_line(XcsvStyle* style, QString line) * a terminating null. Makes multiple calls to that function so * that "ignore to end of line" comments work right. */ -static XcsvStyle -xcsv_parse_style_buff(const char* sbuff) +XcsvStyle +XcsvStyle::xcsv_parse_style_buff(const char* sbuff) { XcsvStyle style; const QStringList lines = QString(sbuff).split('\n'); @@ -1917,8 +1833,8 @@ xcsv_parse_style_buff(const char* sbuff) return style; } -static XcsvStyle -xcsv_read_style(const char* fname) +XcsvStyle +XcsvStyle::xcsv_read_style(const char* fname) { gbfile* fp = gbfopen(fname, "rb", MYNAME); XcsvStyle style; @@ -1942,7 +1858,7 @@ xcsv_read_style(const char* fname) * the xcsv parser and make it ready for general use. */ XcsvStyle -xcsv_read_internal_style(const char* style_buf) +XcsvStyle::xcsv_read_internal_style(const char* style_buf) { XcsvStyle style = xcsv_parse_style_buff(style_buf); @@ -1955,26 +1871,26 @@ xcsv_read_internal_style(const char* style_buf) } void -xcsv_setup_internal_style(const char* style_buf) +XcsvFormat::xcsv_setup_internal_style(const char* style_buf) { intstylebuf = style_buf; } -static void -xcsv_rd_init(const QString& fname) +void +XcsvFormat::rd_init(const QString& fname) { /* * if we don't have an internal style defined, we need to * read it from a user-supplied style file, or die trying. */ if (intstylebuf != nullptr) { - xcsv_style = new XcsvStyle(xcsv_read_internal_style(intstylebuf)); + xcsv_style = new XcsvStyle(XcsvStyle::xcsv_read_internal_style(intstylebuf)); } else { if (!styleopt) { fatal(MYNAME ": XCSV input style not declared. Use ... -i xcsv,style=path/to/file.style\n"); } - xcsv_style = new XcsvStyle(xcsv_read_style(styleopt)); + xcsv_style = new XcsvStyle(XcsvStyle::xcsv_read_style(styleopt)); } if ((xcsv_style->datatype == 0) || (xcsv_style->datatype == wptdata)) { @@ -2004,8 +1920,8 @@ xcsv_rd_init(const QString& fname) assert(gps_datum_wgs84 == GPS_Lookup_Datum_Index("WGS 84")); } -static void -xcsv_rd_deinit() +void +XcsvFormat::rd_deinit() { xcsv_file->stream.close(); delete xcsv_file; @@ -2015,21 +1931,21 @@ xcsv_rd_deinit() xcsv_style = nullptr; } -static void -xcsv_wr_init(const QString& fname) +void +XcsvFormat::wr_init(const QString& fname) { /* * if we don't have an internal style defined, we need to * read it from a user-supplied style file, or die trying. */ if (intstylebuf != nullptr) { - xcsv_style = new XcsvStyle(xcsv_read_internal_style(intstylebuf)); + xcsv_style = new XcsvStyle(XcsvStyle::xcsv_read_internal_style(intstylebuf)); } else { if (!styleopt) { fatal(MYNAME ": XCSV output style not declared. Use ... -o xcsv,style=path/to/file.style\n"); } - xcsv_style = new XcsvStyle(xcsv_read_style(styleopt)); + xcsv_style = new XcsvStyle(XcsvStyle::xcsv_read_style(styleopt)); } xcsv_file = new XcsvFile; @@ -2083,14 +1999,14 @@ xcsv_wr_init(const QString& fname) assert(gps_datum_wgs84 == GPS_Lookup_Datum_Index("WGS 84")); } -static void -xcsv_wr_position_init(const QString& fname) +void +XcsvFormat::wr_position_init(const QString& fname) { - xcsv_wr_init(fname); + wr_init(fname); } -static void -xcsv_wr_deinit() +void +XcsvFormat::wr_deinit() { xcsv_file->stream.close(); delete xcsv_file; @@ -2100,14 +2016,14 @@ xcsv_wr_deinit() xcsv_style = nullptr; } -static void -xcsv_wr_position_deinit() +void +XcsvFormat::wr_position_deinit() { - xcsv_wr_deinit(); + wr_deinit(); } -static void -xcsv_wr_position(Waypoint* wpt) +void +XcsvFormat::wr_position(Waypoint* wpt) { /* Tweak incoming name if we don't have a fix */ switch (wpt->fix) { @@ -2119,26 +2035,9 @@ xcsv_wr_position(Waypoint* wpt) } waypt_add(wpt); - xcsv_data_write(); + write(); waypt_del(wpt); xcsv_file->stream.flush(); } - -ff_vecs_t xcsv_vecs = { - ff_type_internal, - FF_CAP_RW_WPT, /* This is a bit of a lie for now... */ - xcsv_rd_init, - xcsv_wr_init, - xcsv_rd_deinit, - xcsv_wr_deinit, - xcsv_data_read, - xcsv_data_write, - nullptr, - &xcsv_args, - CET_CHARSET_UTF8, 0, /* conversion to utf8 for gmsd is handled by the stream, don't let csv_convert_strings convert gmsd data */ - { nullptr, nullptr, nullptr, xcsv_wr_position_init, xcsv_wr_position, xcsv_wr_position_deinit }, - nullptr - -}; #endif //CSVFMTS_ENABLED diff --git a/xcsv.h b/xcsv.h index 80b089db5..07885b4a5 100644 --- a/xcsv.h +++ b/xcsv.h @@ -21,71 +21,66 @@ #ifndef XCSV_H_INCLUDED_ #define XCSV_H_INCLUDED_ +#include #include // for move #include // for QByteArray +#include // for QDateTime #include // for QList #include // for QString #include // for QStringList +#include // for QVector #include "defs.h" +#include "format.h" +#include "garmin_fs.h" +#include "src/core/datetime.h" // for DateTime #include "src/core/optional.h" // for optional #include "src/core/textstream.h" // for TextStream #if CSVFMTS_ENABLED -/****************************************************************************/ -/* types required for various xcsv functions */ -/****************************************************************************/ +/* + * Class describing an xcsv format. + */ -class XcsvFile { +class XcsvStyle +{ public: - XcsvFile() : mkshort_handle(mkshort_new_handle()) {} - // delete copy and move constructors and assignment operators. - // The defaults are not appropriate, and we haven't implemented proper ones. - XcsvFile(const XcsvFile&) = delete; - XcsvFile& operator=(const XcsvFile&) = delete; - XcsvFile(XcsvFile&&) = delete; - XcsvFile& operator=(XcsvFile&&) = delete; - ~XcsvFile() { - if (mkshort_handle != nullptr) { - mkshort_del_handle(&mkshort_handle); - } - } + /* Types */ - gpsbabel::TextStream stream; - QString fname; - int gps_datum_idx{-1}; /* result of GPS_Lookup_Datum_Index */ - short_handle mkshort_handle{nullptr}; -}; + /* something to map fields to waypts */ + struct field_map { + // We use QByteArrays because consumers want char* data and QByteArrays supply this through constData(). + // If we used QStrings, then we would have to convert to QByteArrays to get the char* data. + // If we use char* then we have to manage memory allocation/deallocation. + // TODO: when consumers use QStrings then we can store QStrings instead of QByteArrays. + QByteArray key; + QByteArray val; + QByteArray printfc; + int hashed_key{0}; + unsigned options{0}; -/* something to map fields to waypts */ -constexpr unsigned options_nodelim = 1; -constexpr unsigned options_absolute = 2; -constexpr unsigned options_optional = 4; + field_map() = default; + field_map(QByteArray k, QByteArray v, QByteArray p, int hk) : key{std::move(k)},val{std::move(v)},printfc{std::move(p)},hashed_key{hk} {} + field_map(QByteArray k, QByteArray v, QByteArray p, int hk, unsigned o) : key{std::move(k)},val{std::move(v)},printfc{ + std::move(p)},hashed_key{hk},options{o} {} + }; -struct field_map { -public: - // We use QByteArrays because consumers want char* data and QByteArrays supply this through constData(). - // If we used QStrings, then we would have to convert to QByteArrays to get the char* data. - // If we use char* then we have to manage memory allocation/deallocation. - // TODO: when consumers use QStrings then we can store QStrings instead of QByteArrays. - QByteArray key; - QByteArray val; - QByteArray printfc; - int hashed_key{0}; - unsigned options{0}; - - field_map() = default; - field_map(QByteArray k, QByteArray v, QByteArray p, int hk) : key{std::move(k)},val{std::move(v)},printfc{std::move(p)},hashed_key{hk} {} - field_map(QByteArray k, QByteArray v, QByteArray p, int hk, unsigned o) : key{std::move(k)},val{std::move(v)},printfc{ - std::move(p)},hashed_key{hk},options{o} {} -}; + /* Constants */ + + static constexpr unsigned options_nodelim = 1; + static constexpr unsigned options_absolute = 2; + static constexpr unsigned options_optional = 4; + + /* Member Functions */ + + static QString xcsv_get_char_from_constant_table(const QString& key); + static XcsvStyle xcsv_read_internal_style(const char* style_buf); + static XcsvStyle xcsv_read_style(const char* fname); + + /* Data Members */ -/* - * Class describing an xcsv format. - */ -struct XcsvStyle { /* PROLOGUE from style file */ /* header lines for writing at the top of the file. */ QStringList prologue; @@ -146,12 +141,214 @@ struct XcsvStyle { /* SHORTWHITE from style file */ gpsbabel_optional::optional whitespace_ok; + +private: + /* Types */ + + /* something to map config file constants to chars */ + struct char_map_t { + const QString key; + const QString chars; + }; + + /* Member Functions */ + + static QString dequote(const QString& in); + static void validate_fieldmap(const field_map& fmp, bool is_output); + static void xcsv_ifield_add(XcsvStyle* style, const QString& qkey, const QString& qval, const QString& qpfc); + static void xcsv_ofield_add(XcsvStyle* style, const QString& qkey, const QString& qval, const QString& qpfc, unsigned int options); + static void xcsv_parse_style_line(XcsvStyle* style, QString line); + static XcsvStyle xcsv_parse_style_buff(const char* sbuff); + + /* Data Members */ + + /* a table of config file constants mapped to chars */ + static const char_map_t xcsv_char_table[]; }; -/* public function prototypes */ +class XcsvFormat : public Format +{ +public: + /* Member Functions */ + QVector* get_args() override + { + return &xcsv_args; + } + + ff_type get_type() const override + { + return ff_type_internal; + } + + QVector get_cap() const override + { + return FF_CAP_RW_WPT; /* This is a bit of a lie for now... */ + } + + QString get_encode() const override + { + return CET_CHARSET_UTF8; + } + + int get_fixed_encode() const override + { + return 0; + } -void xcsv_setup_internal_style(const char* style_buf); -XcsvStyle xcsv_read_internal_style(const char* style_buf); + void rd_init(const QString& fname) override; + void read() override; + void rd_deinit() override; + void wr_init(const QString& fname) override; + void write() override; + void wr_deinit() override; + void wr_position_init(const QString& fname) override; + void wr_position(Waypoint* wpt) override; + void wr_position_deinit() override; + + void xcsv_setup_internal_style(const char* style_buf); + +private: + /* Types */ + + class XcsvFile + { + public: + /* Special Member Functions */ + + XcsvFile() : mkshort_handle(mkshort_new_handle()) {} + // delete copy and move constructors and assignment operators. + // The defaults are not appropriate, and we haven't implemented proper ones. + XcsvFile(const XcsvFile&) = delete; + XcsvFile& operator=(const XcsvFile&) = delete; + XcsvFile(XcsvFile&&) = delete; + XcsvFile& operator=(XcsvFile&&) = delete; + ~XcsvFile() + { + if (mkshort_handle != nullptr) { + mkshort_del_handle(&mkshort_handle); + } + } + + /* Data Members */ + + gpsbabel::TextStream stream; + QString fname; + int gps_datum_idx{-1}; /* result of GPS_Lookup_Datum_Index */ + short_handle mkshort_handle{nullptr}; + }; + + struct xcsv_parse_data { + QString rte_name; + QString trk_name; + bool new_track{false}; + double utm_northing{0}; + double utm_easting{0}; + double utm_zone{0}; + char utm_zonec{'N'}; + UrlLink* link_{nullptr}; + gpsbabel_optional::optional lat_dir_positive; + gpsbabel_optional::optional lon_dir_positive; + }; + + /* Constants */ + + static constexpr char lat_dir(double a) + { + return a < 0.0 ? 'S' : 'N'; + } + static constexpr char lon_dir(double a) + { + return a < 0.0 ? 'W' : 'E'; + } + + /* convert excel time (days since 1900) to time_t and back again */ + static constexpr double excel_to_timet(double a) + { + return (a - 25569.0) * 86400.0; + } + static constexpr double timet_to_excel(double a) + { + return (a / 86400.0) + 25569.0; + } + + static constexpr int gps_datum_wgs84 = 118; // GPS_Lookup_Datum_Index("WGS 84") + + /* Member Functions */ + + static QDateTime yyyymmdd_to_time(const char* s); + static time_t sscanftime(const char* s, const char* format, int gmt); + static time_t addhms(const char* s, const char* format); + static QString writetime(const char* format, time_t t, bool gmt); + static QString writetime(const char* format, const gpsbabel::DateTime& t, bool gmt); + static QString writehms(const char* format, time_t t, int gmt); + static QString writehms(const char* format, const gpsbabel::DateTime& t, int gmt); + static long int time_to_yyyymmdd(const QDateTime& t); + static garmin_fs_t* gmsd_init(Waypoint* wpt); + static void xcsv_parse_val(const QString& value, Waypoint* wpt, const XcsvStyle::field_map& fmp, xcsv_parse_data* parse_data, int line_no); + void xcsv_resetpathlen(const route_head* head); + void xcsv_waypt_pr(const Waypoint* wpt); + QString xcsv_replace_tokens(const QString& original) const; + + /* Data Members */ + + XcsvFile* xcsv_file{nullptr}; + const XcsvStyle* xcsv_style{nullptr}; + double pathdist = 0; + double oldlon = 999; + double oldlat = 999; + + int waypt_out_count = 0; + const route_head* csv_track = nullptr; + const route_head* csv_route = nullptr; + + char* styleopt = nullptr; + char* snlenopt = nullptr; + char* snwhiteopt = nullptr; + char* snupperopt = nullptr; + char* snuniqueopt = nullptr; + char* prefer_shortnames = nullptr; + char* xcsv_urlbase = nullptr; + char* opt_datum = nullptr; + + const char* intstylebuf = nullptr; + + QVector xcsv_args = { + { + "style", &styleopt, "Full path to XCSV style file", nullptr, + ARGTYPE_FILE | ARGTYPE_REQUIRED, ARG_NOMINMAX, nullptr + }, + { + "snlen", &snlenopt, "Max synthesized shortname length", nullptr, + ARGTYPE_INT, "1", nullptr, nullptr + }, + { + "snwhite", &snwhiteopt, "Allow whitespace synth. shortnames", + nullptr, ARGTYPE_BOOL, ARG_NOMINMAX, nullptr + }, + { + "snupper", &snupperopt, "UPPERCASE synth. shortnames", + nullptr, ARGTYPE_BOOL, ARG_NOMINMAX, nullptr + }, + { + "snunique", &snuniqueopt, "Make synth. shortnames unique", + nullptr, ARGTYPE_BOOL, ARG_NOMINMAX, nullptr + }, + { + "urlbase", &xcsv_urlbase, "Basename prepended to URL on output", + nullptr, ARGTYPE_STRING, ARG_NOMINMAX, nullptr + }, + { + "prefer_shortnames", &prefer_shortnames, + "Use shortname instead of description", + nullptr, ARGTYPE_BOOL, ARG_NOMINMAX, nullptr + }, + { + "datum", &opt_datum, "GPS datum (def. WGS 84)", + nullptr, ARGTYPE_STRING, ARG_NOMINMAX, nullptr + }, + }; + +}; #endif // CSVFMTS_ENABLED #endif // XCSV_H_INCLUDED_ -- 2.30.2